import React, { useState } from "react";
import type { UIEvent } from "react";

// 定义虚拟列表组件的props类型, 支持泛型T
interface VirtualListProps<T> {
    data: T[]; //需要渲染的数据数组
    itemHeight: number; //每一项的高度（像素）
    height: number; //列表容器的高度（像素）
    renderItem: (item: T, index: number) => React.ReactNode; //渲染每一项的方法
    buffer?: number; //可选, 缓存区项数, 默认5
}

// 虚拟列表组件, 支持大数据量高性能渲染
function VirtualList<T>({
    data,
    itemHeight,
    height,
    renderItem,
    buffer = 5
}: VirtualListProps<T>) {
    // 可是区域能显示的最大项数
    const visibleCount = Math.ceil(height / itemHeight);

    // 当滚动到起始索引
    const [start, setStart] = useState(0);

    // 滚动事件处理
    const onScroll = (e: UIEvent<HTMLDivElement>) => {
        // 计算当前滚动到的起始项索引
        const scrollTop = (e.target as HTMLDivElement).scrollTop;
        setStart(Math.floor(scrollTop / itemHeight));
    };

    // 计算实际需要渲染的项的起止索引
    const end = Math.min(start + visibleCount + buffer, data.length);
    // 截取可视区域及缓存区的数据
    const renderList = data.slice(start, end);

    // 列表总高度, 用于撑开滚动条
    const totalHeight = data.length * itemHeight;

    // 列表顶部的偏移量, 实现内容的平滑滚动
    const offsetY = start * itemHeight;


    return (
        <div
            style={{ height, overflowY: "auto", position: "relative" }}
            onScroll={onScroll}
        >
            {/* 占位元素，撑起总高度 */}
            <div style={{ height: totalHeight, position: 'relative' }}>
                {/* 实际渲染的可视区域元素 */}
                <div style={{ transform: `translateY(${offsetY}px)` }}>
                    {
                        renderList.map((item, idx) =>
                            renderItem(item, start + idx)
                        )
                    }
                </div>
            </div>
        </div>
    )
}

export default VirtualList;